Jerry's Log

Heap & Stack in Java

contents

스택(Stack)과 힙(Heap)의 상호작용 은 자바 실행 모델에서 가장 중요한 개념 중 하나입니다. 이 원리를 이해하면, 메서드 내부에서 객체의 내용을 바꿀 때(modify)는 원본이 변하지만, 변수에 다른 값을 재할당(reassign)할 때는 원본이 변하지 않는 이유를 명확히 알 수 있습니다.

핵심은 참조(Stack에 있는 주소)실제 데이터(Heap에 있는 객체) 를 구분하는 것입니다.

골든 룰 (The Golden Rule)

자바는 언제나 '값에 의한 전달(Pass-by-Value)'입니다.

메서드에 객체를 넘길 때, 객체 그 자체를 넘기는 것이 아닙니다. 그 객체를 가리키는 메모리 주소(참조)의 복사본을 넘기는 것입니다.


시나리오

main 메서드가 modifyBuilder라는 메서드를 호출하는 상황을 추적해 보겠습니다.

public class MemoryTest {
    public static void main(String[] args) {
        // 라인 1
        StringBuilder sb = new StringBuilder("Hi");
        
        // 라인 2
        modify(sb);
        
        // 라인 3: 여기서 sb는 무엇일까요?
        System.out.println(sb); 
    }

    public static void modify(StringBuilder x) {
        // 라인 4
        x.append(" Java");
        
        // 라인 5
        x = null; 
    }
}

단계별 메모리 상황은 다음과 같습니다.

1단계: main 시작 (라인 1)

스택 (main 프레임) 힙 (Heap)
sb -> @500 주소 @500: StringBuilder 객체 { value: "Hi" }

2단계: 메서드 호출 (라인 2)

이제 서로 다른 두 스택 프레임에 있는 두 변수가 같은 객체를 가리키게 됩니다.

스택 (modify 프레임) 스택 (main 프레임) 힙 (Heap)
x -> @500 sb -> @500 주소 @500: { value: "Hi" }

3단계: 객체 수정 (라인 4)

힙에 있는 객체가 변했습니다. main에 있는 sb도 여전히 @500을 가리키고 있으므로, sb 입장에서도 내용이 바뀐 것으로 보입니다.

스택 (modify 프레임) 스택 (main 프레임) 힙 (Heap)
x -> @500 sb -> @500 주소 @500: { value: "Hi Java" }

4단계: "재할당"의 함정 (라인 5)

스택 (modify 프레임) 스택 (main 프레임) 힙 (Heap)
x -> null sb -> @500 주소 @500: { value: "Hi Java" }

5단계: 메서드 반환 (라인 3)

상호작용 요약

  1. 생성(Creation): new 키워드는 에 메모리를 할당하고 주소를 반환합니다.
  2. 저장(Storage): 스택의 변수는 그 주소(참조)를 저장합니다.
  3. 전달(Passing): 메서드를 호출할 때, 스택은 그 주소를 복사해서 새 프레임에 전달합니다.
  4. 접근(Access): 점 연산자(.)는 JVM에게 스택의 참조를 따라 의 객체로 가서 데이터를 읽거나 쓰라고 명령하는 것입니다.
  5. 범위(Scope): 스택 프레임이 사라지면 그 안의 참조변수도 사라집니다. 힙에 있는 객체는 어떤 스택 변수도 자기를 가리키지 않을 때 가비지 컬렉터(GC) 에 의해 사라집니다.

references